/*
 Copyright 2009 Abdulla Abdurakhmanov (abdulla@latestbit.com)
 Original sources are available at www.latestbit.com

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.

 StdPacketFormer.cpp. Created on: 09.09.2009 12:51:27
 */

#include "smartobjects/coders/StdPacketFormer.hpp"
#include "smartobjects/coders/StdNetworkCommand.hpp"
#include "smartobjects/exceptions/InvalidPacketReceivedException.hpp"

namespace smartobjects {

const unsigned int StdPacketFormer::INITIAL_CAPACITY			=	65535;
const unsigned int StdPacketFormer::MAX_CAPACITY				=	1024 * 1024 * 16; // 16 Mb
const unsigned int StdPacketFormer::INCREMENT_CAPACITY_STEP		=	65535; // 64 Kb step
const unsigned int StdPacketFormer::MAX_PACKET_SIZE				=	StdPacketFormer::MAX_CAPACITY;

StdPacketFormer::StdPacketFormer() :
		m_nCurrentIdx ( 0 ),
		m_nCurrentLen ( 0 ),
		m_bHeaderIsReaded ( false ),
		m_bNeedToClear ( false ),
		m_nCurrentBufferLen ( 0 ),
		m_nCurrentReadPosition ( 0 ) {
	m_poPacketBuffers[0] = new ByteBuffer ( INITIAL_CAPACITY, MAX_CAPACITY );
	m_poPacketBuffers[1] = new ByteBuffer ( INITIAL_CAPACITY, MAX_CAPACITY );
}

StdPacketFormer::~StdPacketFormer() {
	delete m_poPacketBuffers[0];
	delete m_poPacketBuffers[1];
}

void StdPacketFormer::onReceiveRawPacket ( ByteBuffer* buffer ) {
	if( m_nCurrentBufferLen !=0 ) {
		// Save state of buffer
		ByteBuffer* poCurrentBuffer = m_poPacketBuffers[m_nCurrentIdx];

		// Copy second (next) packet data to another buffer
		m_nCurrentIdx =  m_nCurrentIdx == 0 ? 1:0;
		m_poPacketBuffers[m_nCurrentIdx]->clear();
		m_poPacketBuffers[m_nCurrentIdx]->append(
			poCurrentBuffer->getData() + m_nCurrentReadPosition,
			m_nCurrentBufferLen
		);
		poCurrentBuffer->clear();
	}
	m_poPacketBuffers[m_nCurrentIdx]->append(buffer);
	m_nCurrentBufferLen = m_poPacketBuffers[m_nCurrentIdx]->getLen();
	m_nCurrentReadPosition = 0;
}

ByteBuffer* StdPacketFormer::getReadyPacket() {
	ByteBuffer* poCurrentBuffer = m_poPacketBuffers[m_nCurrentIdx];

	if(m_bNeedToClear) {
		poCurrentBuffer->clear();
		m_bNeedToClear = false;
		m_nCurrentLen = 0;
		m_nCurrentReadPosition = 0;
		m_nCurrentBufferLen = 0;
		m_bHeaderIsReaded = false;
		return NULL;
	}

	if(m_nCurrentBufferLen > StdNetworkCommand::HEADER_SIZE && !m_bHeaderIsReaded) {
		m_nCurrentLen = 0;
		memcpy ( (unsigned char*) &m_nCurrentLen,
				poCurrentBuffer->getData()+m_nCurrentReadPosition + StdNetworkCommand::COMMAND_SIZE_POSITION,
				sizeof(boost::uint32_t)
		);
		m_nCurrentLen = OSEndianTools::network_to_host ( m_nCurrentLen );

		if(m_nCurrentLen + StdNetworkCommand::HEADER_SIZE > MAX_PACKET_SIZE) {
			throw InvalidPacketReceivedException(
					boost::format("The received packet is too big: %1%") %m_nCurrentLen
			);
		}
		m_bHeaderIsReaded = true;
	}

	if(m_bHeaderIsReaded){
		unsigned int nPacketSize = m_nCurrentLen + StdNetworkCommand::HEADER_SIZE;
		if( m_nCurrentBufferLen >= nPacketSize ) {
			if(m_nCurrentBufferLen == nPacketSize) {
				// Packet in the current buffer is alone and the buffer need to clear
				// after proceed
				m_bNeedToClear  = true;
			}
			poCurrentBuffer->setPos( m_nCurrentReadPosition );
			poCurrentBuffer->setLen ( m_nCurrentReadPosition + nPacketSize );
			m_bHeaderIsReaded = false;
			m_nCurrentReadPosition+=nPacketSize;
			m_nCurrentBufferLen-=nPacketSize;
			return poCurrentBuffer;
		}
		else
			return NULL;
	}
	else
		return NULL;
}

}
